﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Threading;
namespace TaskSystemClient
{

    public class EnumWindow
    {
        public delegate bool CallBack(IntPtr hwnd, IntPtr lParam);

        [DllImport("user32.dll")]
        public static extern IntPtr FindWindowEx(int hwndParent, int hwndChildAfter,
            string lpszClass, string lpszWindow);

        [DllImport("user32.dll")]
        public static extern IntPtr FindWindow(string strclassName, string strWindowName);


        [DllImport("user32.dll")]
        public static extern int GetLastActivePopup(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

        [DllImport("user32.dll")]
        public static extern int EnumThreadWindows(IntPtr dwThreadId, CallBack lpfn, IntPtr lParam);

        [DllImport("user32.dll")]
        public static extern int EnumWindows(CallBack lpfn, int lParam);

        [DllImport("user32.dll")]
        public static extern int EnumChildWindows(IntPtr hWndParent, CallBack lpfn, IntPtr lParam);

        [DllImport("User32.Dll")]
        public static extern int GetClassName(IntPtr hwnd, StringBuilder lpClassName, int nMaxCount);

        [DllImport("User32.Dll")]
        public static extern int GetWindowThreadProcessId(IntPtr hWnd,out IntPtr lpdwProcessId);

        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        public static extern int SendMessage(
         IntPtr hWnd, //目标窗口的handle
         int Msg, // 消息
         int wParam, // 第一个消息参数
         IntPtr lParam // 第二个消息参数
         );


        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        public static extern int SendMessage_ForText(
         IntPtr hWnd, //目标窗口的handle
         int Msg, // 消息
         int wParam, // 第一个消息参数
         StringBuilder lParam // 第二个消息参数
         );


        [DllImport("kernel32.dll")]
        public static extern Int32 VirtualAllocEx(IntPtr process, Int32 lpaddress, int size, int type, Int32 tect);
        [DllImport("kernel32.dll")]
        public static extern Boolean WriteProcessMemory(IntPtr process, Int32 baseaddress, string buffer, int nsize, int filewriten);
        [DllImport("kernel32.dll")]
        public static extern Boolean ReadProcessMemory(IntPtr hProcess,Int32 lpBaseAddress,byte[] lpBuffer,int nSize,out IntPtr lpNumberOfBytesRead);

        [DllImport("kernel32.dll")]
        public static extern int GetProcAddress(int process, string lpname);
        [DllImport("kernel32.dll")]
        public static extern int GetModuleHandleA(string name);
        [DllImport("kernel32.dll")]
        public static extern IntPtr CreateRemoteThread(IntPtr process, int attrib, int size, Int32 address, int par, int flags, int threadid);
        [DllImport("kernel32.dll")]
        public static extern Int32  WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
        [DllImport("kernel32.dll")]
        public static extern Boolean VirtualFree(Int32 lpAddress, Int32 dwSize, Int32 dwFreeType);
        [DllImport("kernel32.dll")]
        public static extern Boolean VirtualFreeEx(IntPtr process, Int32 lpAddress, Int32 dwSize, Int32 dwFreeType);


        public static UInt32 INFINITE = 0xFFFFFFFF;
        public static Int32 PAGE_EXECUTE_READWRITE = 0x40;
        public static Int32 MEM_COMMIT= 0x1000;
        public static Int32 MEM_RESERVE = 0x2000;
        public static Int32 MEM_RELEASE = 0x8000;


        public static bool SvnWindowProcess(IntPtr hwnd, IntPtr lParam)
        {
            StringBuilder title = new StringBuilder(200);
            int len;
            len = GetWindowText(hwnd, title, 200);
            if(title.ToString().IndexOf("Commit ")!=-1
                ||
                title.ToString().IndexOf("Log Messages ")!=-1
                )
            {
                EnumChildWindows(hwnd, callBackEnumChildWindows, lParam);
            }
            return true;
        }
        public static string Text="";

        public static bool SvnChildWindowProcess(IntPtr hwnd, IntPtr lParam)
        {
            //
            StringBuilder className = new StringBuilder(200);
            GetClassName(hwnd,className,200);
            if(className.ToString()=="Scintilla")
            {
                Text="";
                int length = SendMessage(hwnd, 2183, 0, lParam);
                if(length>0)
                {
                    IntPtr processID;
                    GetWindowThreadProcessId(hwnd,out processID);
                    if(processID==null)
                    {
                        return false;
                    }
                    Process p=Process.GetProcessById(processID.ToInt32());
                    if(p==null)
                    {
                        return false;
                    }
                    Int32 address = VirtualAllocEx(p.Handle, 0, length + 1, MEM_COMMIT + MEM_RESERVE, PAGE_EXECUTE_READWRITE);
                    if (address == 0)
                    {
                        return false;
                    }

                    
                    SendMessage(hwnd, 2182, length+1, new IntPtr(address));
                    byte[] data=new byte[length];
                    IntPtr bytesRead;
                    ReadProcessMemory(p.Handle, address, data, length, out bytesRead);
                    System.Text.UTF8Encoding t=new System.Text.UTF8Encoding();

                    Text = System.Text.Encoding.UTF8.GetString(data);

                    VirtualFreeEx(p.Handle,address, 0, MEM_RELEASE);                    

                    p.Dispose();
                }
                return false;
            }
            else if (className.ToString() == "RichEdit20W")
            {
                int length = SendMessage(hwnd, Win32SDK.Constants.WM_GETTEXTLENGTH, 0, lParam);
                if(length>0)
                {
                    StringBuilder str=new StringBuilder(length+1);
                    SendMessage_ForText(hwnd, Win32SDK.Constants.WM_GETTEXT, length + 1,str);
                    Text = str.ToString();
                }
                
                return false;
            }
            return true;
        }


        public static CallBack callBackEnumWindows = new CallBack(SvnWindowProcess);
        public static CallBack callBackEnumChildWindows = new CallBack(SvnChildWindowProcess);

        //调用下面的这个函数能够获得SVN日志窗口的文字。
        // EnumWindow.EnumWindows(EnumWindow.callBackEnumWindows, 0);

    }

    public struct COPYDATASTRUCT
    {
       public IntPtr dwData;
       public int cbData;
       [MarshalAs(UnmanagedType.LPStr)] public string lpData;
    }

    public class ExternCommandHelper
    {

        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        private static extern int  MySendMessage(
         IntPtr hWnd, //目标窗口的handle
         int Msg, // 消息
         int wParam, // 第一个消息参数
         ref COPYDATASTRUCT lParam // 第二个消息参数
         );

        /**
         * 从COPY_DATA消息中解析外部命令，这个命令应该由另一个进程或其他程序通过 SendExternCommand 发送的
         * 
         */
        public static string[] RecvExternCommand(ref System.Windows.Forms.Message m)
        {
            COPYDATASTRUCT mystr = new COPYDATASTRUCT();
            Type mytype = mystr.GetType();
            mystr = (COPYDATASTRUCT)m.GetLParam(mytype);
            return mystr.lpData.Split(' ');
        }

        /**
         * 把当前程序的命令行参数发送给主进程
         * 当已经启动一个SPM的时候，再次启动第二个SPM并附带命令行参数，则相当于向第一个进程传递一个消息。
         * 利用这个特性可以和外部程序写作，比如 SVN
         */
        public static void SendExternCommand(string[] args)
        {
            IntPtr handler = Win32SDK.User32.FindWindow(null, @"SPM");
            if (handler != null)
            {
                string commandLine = string.Join(" ", args);
                byte[] sarr = System.Text.Encoding.Default.GetBytes(commandLine);
                int len = sarr.Length;
                COPYDATASTRUCT cds;
                cds.dwData = (IntPtr)100;
                cds.lpData = commandLine;
                cds.cbData = len + 1;
                MySendMessage(handler, Win32SDK.Constants.WM_COPYDATA, 0, ref cds);
            }
        }

        public static bool ParseCommand(string[] args,bool run)
        {
            try
            {
                if (args.Length < 1)
                    return false;
                IExternCommand cmd = null;
                string firstArg = args[0];
                if (firstArg.StartsWith("spm://"))
                {
                    Regex r = new Regex(@"spm:///([0-9]+)");
                    Match m = r.Match(firstArg);
                    if (m.Success)
                    {

                        int task_id = int.Parse(m.Groups[1].Value);

                        ViewTaskExternCommand sdc = new ViewTaskExternCommand();
                        sdc.TaskID = task_id;
                        cmd = sdc;
                    }
                    else
                    {
                        GameEditor.Common.Dialogs.Error("无法识别的命令行参数！[" + string.Join(" ", args) + "] spm协议支持如下格式 spm:///任务ID 比如 spm:///1235", "SPM");
                    }
                }
                else if (firstArg.StartsWith(@"spmsvn://"))
                {
                    Regex r = new Regex(@"spmsvn://(log)/(.+)\:([0-9]+)");
                    Match m = r.Match(firstArg);
                    if (m.Success)
                    {
                        SvnLogExternCommand sec = new SvnLogExternCommand();
                        sec.Path = m.Groups[2].Value;
                        sec.Revision = int.Parse(m.Groups[3].Value);

                        cmd = sec;
                    }
                    else
                    {
                        GameEditor.Common.Dialogs.Error("无法识别的命令行参数！[" + string.Join(" ", args) + "] spm协议支持如下格式 spm:///任务ID 比如 spm:///1235", "SPM");
                    }
                    
                }
                else if (firstArg.Equals("svn"))
                {
                    if (args.Length < 2)
                    {
                        GameEditor.Common.Dialogs.Error("无法识别的命令行参数！[" + string.Join(" ", args) + "] svn 命令后面至少要包含一个命令或事件，比如 svn post-commit ....", "SPM");

                        return false;
                    }

                    string command = args[1];
                    if (command.Equals("post-commit"))
                    {
                        //Post-commit  file_list_file depth message_file depth error_file path

                        if (args.Length < (6 + 2))
                        {
                            GameEditor.Common.Dialogs.Error("无法识别的命令行参数！[" + string.Join(" ", args) + "] svn post-commit 命令需要额外的5个参数。", "SPM");
                            return false;
                        }

                        SvnEventScriptExternCommand secec = new SvnEventScriptExternCommand();
                        secec.EventName = command;
                        secec.ListFile = args[2];
                        secec.Depth = int.Parse(args[3]);
                        secec.MessageFile = args[4];
                        secec.Revision = int.Parse(args[5]);
                        secec.ErrorFile = args[6];
                        secec.Path = args[7];
                        secec.ProcessInputData();
                        cmd = secec;
                    }
                    else
                    {
                        GameEditor.Common.Dialogs.Error("无法识别的命令行参数！[" + string.Join(" ", args) + "] spm目前只支持 svn 的 post-commit 事件", "SPM");
                        return false;
                    }

                }
                else
                {
                    GameEditor.Common.Dialogs.Error("无法识别的命令行参数！[" + string.Join(" ", args) + "]", "SPM");
                }
                if (cmd != null)
                {
                    if (run)
                    {
                        cmd.run();
                    }
                    else
                    {
                        UIThread.AddScheduledOperation(new ScheduledOperation(
                            delegate()
                            {
                                cmd.run();
                            }
                        ));
                    }
                }
                else
                {
                    GameEditor.Common.Dialogs.Error("无法识别的命令行参数！[" + string.Join(" ", args) + "]", "SPM");
                }
                return false;
            }
            catch (System.Exception ex)
            {
                GameEditor.Common.Dialogs.Error("错误的命令行参数！[" + string.Join(" ", args) + "] [" + ex.Message + "]", "SPM");
            }
            return false;
        }
    }

    public interface IExternCommand
    {
        bool run();
    }

    public class ViewTaskExternCommand : IExternCommand
    {
        public int TaskID;

        public bool run()
        {
            //GameEditor.Common.Dialogs.Error("Task ID is "+TaskID.ToString(), "SPM");
            TaskInformation info = new TaskInformation();
            info.LoadTaskByID(TaskID);
            if(info.CurrentTask!=null)
            {
                //if(Command=="svn")
                //{
                //    if (info.CurrentTask.State == "open" || info.CurrentTask.State == "new" || info.CurrentTask.State == "reopen")
                //    {
                //        info.CurrentTask.State = "done";
                //        info.TaskStateToUI(info.CurrentTask);

                //        EnumWindow.EnumWindows(EnumWindow.callBackEnumWindows, 0);
                //        string msg = "<br>"+EnumWindow.Text;
                //        msg=""; //目前不包含这个消息，因为这个和SPM标题重复
                //        info.CurrentTask.Description = Global<MainForm>.Instance.UserName + " " + DateTime.Today.ToLongDateString() + " 提交到SVN " +msg+"<br/><br/>" + info.CurrentTask.Description ?? "";
                //    }
                //}
                info.Show();
                info.BringToFront();
            }
            return true;
        }
    }

    public class SvnLogExternCommand : IExternCommand
    {
        protected bool SvnLogWindowProcess(IntPtr hwnd, IntPtr lParam)
        {
            StringBuilder title = new StringBuilder(200);
            int len;
            len = EnumWindow.GetWindowText(hwnd, title, 200);
            string title_text = title.ToString();
            title_text = title_text.ToLower();
            string temp_path = Path.Replace("\\\\", "\\");
            temp_path = temp_path.Replace("//", "\\");
            temp_path = temp_path.Replace("/", "\\");
            temp_path = temp_path.ToLower();
            string full_title=string.Format("log messages - {0}",temp_path);
            if (title_text.IndexOf(full_title) != -1
                )
            {
                Win32SDK.User32.SetActiveWindow(hwnd);
                EnumWindow.EnumChildWindows(hwnd, callBackEnumChildWindows, lParam);
            }
            return true;
        }
        protected bool SvnLogChildWindowProcess(IntPtr hwnd, IntPtr lParam)
        {
            //
            StringBuilder className = new StringBuilder(200);
            EnumWindow.GetClassName(hwnd, className, 200);
            if (className.ToString() == "Edit")
            {
                IntPtr processID;
                EnumWindow.GetWindowThreadProcessId(hwnd, out processID);
                if (processID == null)
                {
                    return false;
                }
                Process p = Process.GetProcessById(processID.ToInt32());
                if (p == null)
                {
                    return false;
                }
                byte[] data = System.Text.Encoding.UTF8.GetBytes(Revision.ToString());
                int length = data.Length;
                Int32 address = EnumWindow.VirtualAllocEx(p.Handle, 0, length+1, EnumWindow.MEM_COMMIT + EnumWindow.MEM_RESERVE, EnumWindow.PAGE_EXECUTE_READWRITE);
                if (address == 0)
                {
                    return false;
                }


                EnumWindow.WriteProcessMemory(p.Handle, address, Revision.ToString(), length, 0);
                //wm_settext 0x000c
                EnumWindow.SendMessage(hwnd, 0x000C, length, new IntPtr(address));
                EnumWindow.VirtualFreeEx(p.Handle, address, 0, EnumWindow.MEM_RELEASE);

                p.Dispose();
                return false;
            }
            return true;
        }

        protected EnumWindow.CallBack callBackEnumWindows = null;
        protected EnumWindow.CallBack callBackEnumChildWindows = null;

        public SvnLogExternCommand()
        {
            callBackEnumWindows = new EnumWindow.CallBack(SvnLogWindowProcess);
            callBackEnumChildWindows = new EnumWindow.CallBack(SvnLogChildWindowProcess);
        }

        public string Path="";
        public int Revision=-1;

        public bool run()
        {
            FileTypeRegInfo info=FileTypeRegister.GetFileTypeRegInfo("tsvn","tsvn");
            string svnProc=@"C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe";
            if(!string.IsNullOrEmpty(info.ExePath))
            {
                svnProc=info.ExePath;
                int pos = svnProc.IndexOf("/command");
                if (pos != -1)
                {
                    svnProc = svnProc.Remove(pos);
                }
            }
            string args = "/command:log /path:\""+Path+"\" c:\\ \"spm view svn log\" ";
            Process.Start(svnProc, args);
            Thread.Sleep(500);

            //EnumWindow.EnumWindows(callBackEnumWindows, 0);

            return true;
        }
    }

    public class SvnEventScriptExternCommand : IExternCommand
    {
        public string EventName;
        public string ListFile;
        public int Depth;
        public string MessageFile;
        public int Revision;
        public string ErrorFile;
        public string Path;


        private string message_file_text="";
        public void ProcessInputData()
        {

            try
            {
                message_file_text = System.IO.File.ReadAllText(MessageFile, Encoding.Default);
            }
            catch (Exception e)
            {
                GameEditor.Common.Dialogs.Error("处理SVN事件失败[" + EventName + "] " + e.Message, "SPM");
            }
        }

        public bool run()
        {
            if (EventName.CompareTo("post-commit") != 0)
            {
                GameEditor.Common.Dialogs.Error("处理SVN事件失败[" + EventName + "] 目前只支持post-commit", "SPM");
                return true;
            }

            if (string.IsNullOrEmpty(message_file_text))
            {
                return true;
            }
            Regex r = new Regex(@"spm:([0-9]+)",RegexOptions.IgnoreCase|RegexOptions.Compiled);
            MatchCollection mc = r.Matches(message_file_text);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < mc.Count; ++i)
            {
                try
                {
                    Match m = mc[i];
                    int taskID = int.Parse(m.Groups[1].Value);
                    TaskData taskData = TaskManager.Instance.GetTaskDataFromCacheOrDB(taskID);
                    if (taskData == null)
                    {
                        sb.Append(string.Format("任务[{0}]不存在\n", taskID));
                        continue;
                    }
                    if (taskData.Task.State == "open" || taskData.Task.State == "new" || taskData.Task.State == "reopen")
                    {
                        TaskInformation info = new TaskInformation();
                        info.LoadTaskByID(taskID);
                        info.CurrentTask.State = "done";
                        info.TaskStateToUI(info.CurrentTask);
                        Path=Path.Replace("\\\\", "/");
                        Path=Path.Replace("\\", "/");
                        Path = Path.Replace("'", "\"");
                        string msg = string.Format("<a href='spmsvn://log/{3}:{2}' target='_parent' >svn revision:{2}  {3}  by {0} on {1}(右键点击)</a> <br/><br/>", Global<MainForm>.Instance.UserName, DateTime.Today.ToLongDateString(), Revision, Path);
                        info.CurrentTask.Description = msg + info.CurrentTask.Description ?? "";

                        info.Show();
                        info.BringToFront();
                    }
                    else
                    {
                        sb.Append(string.Format("任务SPM:{0} {1}已经完成了\n", taskID,taskData.Task.Title));
                    }

                }
                catch(Exception e)
                {
                    GameEditor.Common.Dialogs.Error("处理SVN事件失败["+EventName+"] "+e.Message, "SPM");
                }

            }
            if (sb.Length > 0)
            {
                GameEditor.Common.Dialogs.Error(sb.ToString(), "SPM");
            }
            return true;
        }
    }

}
